package com.topscomm.mis.cbb.pm.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.topscomm.basic.BasicEntity;
import com.topscomm.basic.exception.BusinessException;
import com.topscomm.basic.exception.MessageException;
import com.topscomm.basic.exception.ServiceException;
import com.topscomm.basic.util.PrimaryKeyUtil;
import com.topscomm.cbo.pojo.*;
import com.topscomm.cbo.service.*;
import com.topscomm.cbo.service.impl.CboApprovalNodeInsService;
import com.topscomm.cbo.service.impl.CboApprovalNodeService;
import com.topscomm.mis.cbb.pm.api.ITaskService;
import com.topscomm.mis.cbb.pm.base.pojo.*;
import com.topscomm.mis.cbb.pm.base.service.ICbbPmTaskBackRecordService;
import com.topscomm.mis.cbb.pm.base.service.ICbbPmTaskConstraintService;
import com.topscomm.mis.cbb.pm.base.service.ICbbPmTaskService;
import com.topscomm.mis.cbb.pm.base.service.ICbbPmWbsService;
import com.topscomm.mis.cbb.pm.base.service.impl.CbbPmFlowSkipService;
import com.topscomm.mis.cbb.pm.base.service.impl.CbbPmTaskService;
import com.topscomm.mis.cbb.pm.enumerate.TaskStatusEnum;
import com.topscomm.mis.cbb.pm.service.abs.AbstractFlowSkipService;
import com.topscomm.mis.cbb.pm.service.abs.AbstractTaskService;
import com.topscomm.mis.cbb.pm.service.chain.TaskCompleteHandler;
import com.topscomm.mis.cbb.pm.service.chain.handler.CompetitionHandler;
import com.topscomm.mis.cbb.pm.service.chain.handler.CountersignHandler;
import com.topscomm.mis.cbb.pm.service.chain.handler.OtherHandler;
import com.topscomm.mis.cbb.pm.util.FlowNode;
import com.topscomm.mis.cbb.pm.util.PmSystemConst;
import com.topscomm.pub.util.ConvertUtil;
import com.topscomm.pub.util.StringBufferProxy;
import com.topscomm.pub.util.StringUtil;
import com.topscomm.tap.TapEntity;
import com.topscomm.tap.common.ParameterCache;
import com.topscomm.tap.common.SessionUserBean;
import com.topscomm.tap.common.TapSystemConst;
import com.topscomm.tap.util.ErrorLogUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Service
public class FlowSkipService extends AbstractFlowSkipService {
    @Autowired
    private CbbPmFlowSkipService cbbPmFlowSkipService;
    @Autowired
    private ITaskService taskService;
    @Autowired
    private CboApprovalNodeInsService cboApprovalNodeInsService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    public final static String flowSkipRedisKey = "CbbPm:flowSkip:runningFlowInsList:";
    @Resource(name = "cbbThreadPool")
    private ExecutorService cbbThreadPool;
    @Autowired
    private CboApprovalNodeService cboApprovalNodeService;
    @Autowired
    private CbbPmTaskService cbbPmTaskService;
    @Autowired
    private ICbbPmWbsService cbbPmWbsService;
    /**
     * @description:保存数据
     * @param paraMap
     * @author: zhanglongfei
     * @date: 2021年2月26日下午5:45:29
     * @modify:
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveData(Map<String, Object> paraMap) {
        String projectuucode = ConvertUtil.convertToString(paraMap.get("projectuucode"));
        CbbPmWbsEntity wbsEntity = cbbPmWbsService.queryObjectByWhere(" projectuucode='"+projectuucode+"'");
        String nodeIdListStr = ConvertUtil.convertToString(paraMap.get("idlist"));
        String flowId = ConvertUtil.convertToString(wbsEntity.getFlowid());
        String[] nodeIdList = nodeIdListStr.split(",");
        //判断新增的审批节点,已经已经关闭的审批节点,已经关闭的审批节点不能取消
        List<CbbPmFlowSkipEntity> flowSkipEntities = cbbPmFlowSkipService.queryByWhere("flowid='" + flowId + "' and "+ "projectuucode='" + projectuucode + "'");
        //新增的节点
        List<String> addNodeId = new ArrayList<>();
        for (String idStr : nodeIdList) {
            if (!flowSkipEntities.stream().anyMatch(m -> m.getNodeid().equals(idStr))) {
                addNodeId.add(idStr);
            }
        }
        paraMap.put("whereSql", " nodeid in (" + StringUtils.join(addNodeId, ",") + ")");
        List<Map<String, Object>> list = cboApprovalNodeService.queryMap(paraMap, "com.topscomm.mis.cbb.pm.base.pojo.CbbPmFlowSkipEntity.queryFlowNodeWithTask");
        list = list.stream().filter(item -> ConvertUtil.convertToInt(item.get("executenum")) > 0).collect(Collectors.toList());
        if (list.size() > 0) {
            throw new MessageException("裁剪节点["+list.get(0).get("name")+"]存在已经开始任务的节点,请去除该节点再保存");
        }
        //取消裁剪的节点
        List<String> cancelNodeId = new ArrayList<>();
        for (CbbPmFlowSkipEntity flowSkipEntity : flowSkipEntities) {
            if (!nodeIdListStr.contains(flowSkipEntity.getNodeid())) {
                cancelNodeId.add(flowSkipEntity.getNodeid());
            }
        }
        paraMap.put("whereSql", " nodeid in (" + StringUtils.join(cancelNodeId, ",") + ")");
        list = cboApprovalNodeService.queryMap(paraMap, "com.topscomm.mis.cbb.pm.base.pojo.CbbPmFlowSkipEntity.queryFlowNodeWithTask");
        list = list.stream().filter(item -> ConvertUtil.convertToInt(item.get("executenum")) > 0).collect(Collectors.toList());
        if (list.size() > 0) {
            throw new MessageException("取消裁剪的节点["+list.get(0).get("name")+"]存在已经开始任务的节点,无法取消裁剪,请重新设置");
        }
        cbbPmFlowSkipService.deleteByWhere("flowid='" + flowId + "' and "+ "projectuucode='" + projectuucode + "'");
        if (StringUtil.isEmpty(nodeIdListStr)) {
            return;
        }

        List<BasicEntity> insertList = new ArrayList<>();
        for (String idStr : nodeIdList) {
            CbbPmFlowSkipEntity entity = new CbbPmFlowSkipEntity();
            entity.setFlowid(ConvertUtil.convertToLong(flowId));
            entity.setNodeid(idStr);
            entity.setProjectuucode(projectuucode);
            insertList.add(entity);
        }
        cbbPmFlowSkipService.insertBatch(insertList);
        this.updateSkipNodeTask(projectuucode,nodeIdListStr,cancelNodeId);
    }

    private void updateSkipNodeTask(String projectuucode, String nodeIdListStr, List<String> cancelNodeId) {
        StringBufferProxy whereSql = new StringBufferProxy();
        whereSql.appendSingle(" {0}='{1}' ", CbbPmTaskEntity.FieldProjectuucode, projectuucode);
        whereSql.appendSingle(" and {0} in ({1}) ", CbbPmTaskEntity.FieldCurrently, PmSystemConst.CboYesNo.YES);
        whereSql.appendSingle(" and {0} in ({1}) ", CbbPmTaskEntity.FieldNodeid, nodeIdListStr);
        whereSql.appendSingle(" and {0} in ({1}) ", CbbPmTaskEntity.FieldStatus, StringUtils.join(Arrays.asList(PmSystemConst.TaskStatus.NOTSTART, PmSystemConst.TaskStatus.READYSTART, PmSystemConst.TaskStatus.CONFUSION), ","));
        Map<String, Object> updateMap = new HashMap<>();
        updateMap.put(CbbPmTaskEntity.FieldStatus, TaskStatusEnum.CUT.getNumber());
        cbbPmTaskService.updateByWhere(updateMap, whereSql.toString());
        if (cancelNodeId.size() > 0) {
            whereSql.clear();
            whereSql.appendSingle(" {0}='{1}' ", CbbPmTaskEntity.FieldProjectuucode, projectuucode);
            whereSql.appendSingle(" and {0} in ({1}) ", CbbPmTaskEntity.FieldCurrently, PmSystemConst.CboYesNo.YES);
            whereSql.appendSingle(" and {0} in ({1}) ", CbbPmTaskEntity.FieldNodeid,  StringUtils.join(cancelNodeId, ","));
            List<CbbPmTaskEntity> pmTaskEntities = cbbPmTaskService.queryByWhere(whereSql.toString());
            List<BasicEntity> updateEntities = new ArrayList<>();
            for (CbbPmTaskEntity pmTaskEntity : pmTaskEntities) {
                if (StringUtil.isEmpty(pmTaskEntity.getResponsibleusercode())) {
                    pmTaskEntity.setStatus(TaskStatusEnum.CONFUSION.getNumber());
                } else {
                    pmTaskEntity.setStatus(TaskStatusEnum.NOTSTART.getNumber());
                }
                updateEntities.add(pmTaskEntity);
            }
            cbbPmTaskService.updateBatch(updateEntities);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void afterApproveSkipFlow(Map<String, Object> docInfo, CboApprovalNodeInsEntity nodeIns, CboApprovalNodeEntity node) {
        if (TapSystemConst.TapApprovalNodeStateEnum.Approved != nodeIns.getState()) {
            return;
        }
        // 跳过节点的实现逻辑为：
        // 1、获取当前所有未审批的节点实例NodeIns，判断每个实例，是否在要跳过的节点里；
        // 2、若在跳过的节点配置里，那么执行节点通过操作；
        // 3、对于跳过的节点实例，要将发送的Message都删除掉；
        // 4、向BaseDataOperateState表中插入数据，用于屏蔽掉跳过的节点tab，防止出现空页面；
        StringBufferProxy where = new StringBufferProxy();
        where.appendLineSingle("{0}='{1}'", CboApprovalNodeInsEntity.FieldFlowinsid, nodeIns.getFlowinsid());
        where.appendLineSingle(" and {0}='{1}'", CboApprovalNodeInsEntity.FieldState, TapSystemConst.TapApprovalNodeStateEnum.Approveing);
        // 增加这个，是因为有时候有前后两个节点都需要跳过，跳过第一个后，会触发跳过第二个，跳过第二个时，不要加载之前还没处理完的未审批
        where.appendLineSingle(" and id>{0}", nodeIns.getId());
        where.appendLineSingle(" and enabled=1");
        List<CboApprovalNodeInsEntity> list = cboApprovalNodeInsService.queryByWhere(where.toString());
        where.clear();
        where.appendLineSingle("flowid='{0}' and projectuucode='{1}' and enabled=1 ", nodeIns.getFlowid(), docInfo.get("projectuucode"));
        List<CbbPmFlowSkipEntity> skipList = cbbPmFlowSkipService.queryByWhere(where.toString());
        String approvalOpinion = ParameterCache.getValue("ApprovalSkipOpinion", "节点无需办理，系统自动跳过！");
        List<CboApprovalNodeInsEntity> needSkipNodeList = new ArrayList<>();
        for (CboApprovalNodeInsEntity cboApprovalNodeInsEntity : list) {
            if (skipList.stream().anyMatch(m -> !StringUtil.isEmpty(m.getNodeid()) && m.getNodeid().equals(ConvertUtil.convertToString(cboApprovalNodeInsEntity.getNodeid())))) {
                needSkipNodeList.add(cboApprovalNodeInsEntity);
            }
        }
        if (needSkipNodeList.size() > 0) {
            // 如果有跳过的节点，那么就开启一个线程执行跳过逻辑
            if (inThreadProcess(nodeIns.getFlowinsid())) {
                for (CboApprovalNodeInsEntity cboApprovalNodeInsEntity : needSkipNodeList) {
                    execNodeSkip(cboApprovalNodeInsEntity, approvalOpinion);
                }
                return;
            }
            try {
                List<Future> futureTaskList = new ArrayList<Future>();

                futureTaskList.add(cbbThreadPool.submit(() -> {
                    try {
                        redisTemplate.opsForValue().set(flowSkipRedisKey + nodeIns.getFlowinsid(), "1");
                        for (CboApprovalNodeInsEntity cboApprovalNodeInsEntity : needSkipNodeList) {
                            execNodeSkip(cboApprovalNodeInsEntity, approvalOpinion);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new ServiceException(e.getMessage());
                    } finally {
                        redisTemplate.delete(flowSkipRedisKey + nodeIns.getFlowinsid());
                    }
                }));
            } catch (Exception e) {
                throw new ServiceException("审批过程出现异常，请联系管理员处理！");
            }
        }
    }

    private boolean inThreadProcess(long flowInsId) {
        Object storeKey = redisTemplate.opsForValue().get(flowSkipRedisKey + flowInsId);
        return storeKey != null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void execNodeSkip(CboApprovalNodeInsEntity nodeIns, String approvalOpinion) {
        CboApprovalNodeInsEntity newInsEntity = cboApprovalNodeInsService.queryById(nodeIns.getId() + "");
        if (newInsEntity.getState() != TapSystemConst.TapApprovalNodeStateEnum.Approveing) {
            return;
        }
        SessionUserBean userBean = taskService.generateSessionUserBean("", nodeIns.getApprovaluserid());
        cbbPmFlowSkipService.executeApprove(nodeIns.getId() + "", new Date(), approvalOpinion, "1", "0",
                userBean, 0L);
        //todo::删除消息提醒数据
        // deleteSendMessage(nodeIns);
    }
}
